/*
    This file is part of Slydini.

    Slydini is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Slydini is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Slydini.  If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include "iplimage_convert.h"

inline int align(int size, int align)
{
	return (size + align - 1) & -align;

}

IplImage *QImageToIplImage(const QImage * qImage)
{
	int width = qImage->width();
	int height = qImage->height();

	CvSize Size;
	Size.height = height;
	Size.width = width;

	IplImage *charIplImageBuffer = cvCreateImage(Size, IPL_DEPTH_8U,
			1);
	char *charTemp = (char *) charIplImageBuffer->imageData;

	for (int y = 0; y < height; y++)
	{
		for (int x = 0; x < width; x++)
		{
			int index = y * width + x;

			charTemp[index] = (char) qGray(qImage->pixel(x, y));
		}
	}

	return charIplImageBuffer;

}

/* Usage:
   QImage *qi = IplImageToQImage(iplCurrentImage, &data);
   QPixmap pix = QPixmap(*qi);
   delete qi;
   if(data)
   free(data);
   cvReleaseImage(&iplCurrentImage);
   */
QImage *IplImageToQImage(const IplImage * iplImage, uchar *&qImageBuffer, uchar **data, double mini, double maxi) {

	int width = iplImage->width;

	/* Note here that OpenCV image is stored so that each lined is
	   32-bits aligned thus
	 * explaining the necessity to "skip" the few last bytes of each
	 line of OpenCV image buffer.
	 */
	int widthStep = iplImage->widthStep;
	int height = iplImage->height;

	switch (iplImage->depth) {

		case IPL_DEPTH_8U:

			if (iplImage->nChannels == 1) {

				/* OpenCV image is stored with one byte grey pixel. We convert it
				   to an 8 bit depth QImage.
				   */

				qImageBuffer = (uchar *) malloc(width*height*sizeof(uchar));
				uchar *QImagePtr = qImageBuffer;
				const uchar *iplImagePtr = (const uchar *) iplImage->imageData;

				for (int y = 0; y < height; y++) {

					// Copy line by line
					memcpy(QImagePtr, iplImagePtr, width);
					QImagePtr += width;
					iplImagePtr += widthStep;

				}

			} else if (iplImage->nChannels == 3) {

				/* OpenCV image is stored with 3 byte color pixels (3 channels).
				   We convert it to a 32 bit depth QImage.
				   */

				qImageBuffer = (uchar *) malloc(width*height*4*sizeof(uchar));
				uchar *QImagePtr = qImageBuffer;
				const uchar *iplImagePtr = (const uchar *) iplImage->imageData;

				for (int y = 0; y < height; y++) {

					for (int x = 0; x < width; x++) {

						// We cannot help but copy manually.

						QImagePtr[0] = iplImagePtr[0];
						QImagePtr[1] = iplImagePtr[1];
						QImagePtr[2] = iplImagePtr[2];
						QImagePtr[3] = 0;

						QImagePtr += 4;
						iplImagePtr += 3;

					}

					iplImagePtr += widthStep-3*width;

				}

			} else {
				qDebug("IplImageToQImage: image format is not supported : depth=8U and %d channels\n", iplImage->nChannels);
			}

			break;

		case IPL_DEPTH_16U:

			if (iplImage->nChannels == 1) {

				/* OpenCV image is stored with 2 bytes grey pixel. We convert it
				   to an 8 bit depth QImage.
				   */

				qImageBuffer = (uchar *) malloc(width*height*sizeof(uchar));
				uchar *QImagePtr = qImageBuffer;
				const uint16_t *iplImagePtr = (const uint16_t *)
					iplImage->imageData;

				for (int y = 0; y < height; y++) {

					for (int x = 0; x < width; x++) {
						// We take only the highest part of the 16 bit value. It is similar to dividing by 256.
							*QImagePtr++ = ((*iplImagePtr++) >> 8);

					}

					iplImagePtr += widthStep/sizeof(uint16_t)-width;

				}

			} else {

				qDebug("IplImageToQImage: image format is not supported : depth=16U and %d channels\n", iplImage->nChannels);

			}

			break;

		case IPL_DEPTH_32F:

			if (iplImage->nChannels == 1) {

				/* OpenCV image is stored with float (4 bytes) grey pixel. We
				   convert it to an 8 bit depth QImage.
				   */

				qImageBuffer = (uchar *) malloc(width*height*sizeof(uchar));
				uchar *QImagePtr = qImageBuffer;
				const float *iplImagePtr = (const float *) iplImage->imageData;

				for (int y = 0; y < height; y++) {

					for (int x = 0; x < width; x++) {

						uchar p;
						float pf = 255 * ((*iplImagePtr++) - mini) / (maxi - mini);

						if (pf < 0) p = 0;
						else if (pf > 255) p = 255;
						else p = (uchar) pf;

						*QImagePtr++ = p;

					}

					iplImagePtr += widthStep/sizeof(float)-width;

				}

			} else {

				qDebug("IplImageToQImage: image format is not supported : depth=32F and %d channels\n", iplImage->nChannels);

			}

			break;

		case IPL_DEPTH_64F:

			if (iplImage->nChannels == 1) {

				/* OpenCV image is stored with double (8 bytes) grey pixel. We
				   convert it to an 8 bit depth QImage.
				   */

				qImageBuffer = (uchar *) malloc(width*height*sizeof(uchar));
				uchar *QImagePtr = qImageBuffer;
				const double *iplImagePtr = (const double *) iplImage->imageData;

				for (int y = 0; y < height; y++) {

					for (int x = 0; x < width; x++) {

						uchar p;
						double pf = 255 * ((*iplImagePtr++) - mini) / (maxi - mini);

						if (pf < 0) p = 0;
						else if (pf > 255) p = 255;
						else p = (uchar) pf;

						*QImagePtr++ = p;

					}

					iplImagePtr += widthStep/sizeof(double)-width;

				}

			} else {

				qDebug("IplImageToQImage: image format is not supported : depth=64F and %d channels\n", iplImage->nChannels);

			}

			break;

		default:

			qDebug("IplImageToQImage: image format is not supported : depth=%d and %d channels\n", iplImage->depth, iplImage->nChannels);

	}

	QImage *qImage;

	if (iplImage->nChannels == 1) {
		// We should check who is going to destroy this allocation.
		QRgb *colorTable = new QRgb[256];

		for (int i = 0; i < 256; i++)
			colorTable[i] = qRgb(i, i, i);

		//qImage = new QImage(qImageBuffer, width, height, 8, colorTable, 256);
		qImage = new QImage(qImageBuffer, width, height, QImage::Format_Mono);
	} else {
		//qImage = new QImage(qImageBuffer, width, height, 32, NULL, 256);
		qImage = new QImage(qImageBuffer, width, height, QImage::Format_RGB32);

	}


	if (data)
		*data = qImageBuffer;
//	printf("t:%d\n",qImageBuffer);
//    if (qImageBuffer!=NULL)
//		delete qImageBuffer;
//	printf("here\n");
//	fflush(0);

	return qImage; 
}
